﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MegavaniaX.World;
using MegavaniaX.Messaging;
using MegavaniaX.Messaging.Events;

namespace MegavaniaX.Actors.States
{
    /// <summary>
    /// Base player state class, contains common functionality among all states.
    /// </summary>
    public abstract class PlayerStateBase : IState
    {
        protected Player _player;
        public virtual IState NextState { get; protected set; }
        public virtual IState Substate { get; protected set; }

        public PlayerStateBase(Player player)
        {
            _player = player;
        }     

        public void AddSubstate(IState state)
        {
            Substate = state;
        }

        public abstract void OnEnter();

        public virtual bool Update(TimeSpan elapsedTime, Screen screen)
        {
            _player.PushActorOutOfWalls(screen);

            _player.velocity.X += _player.acceleration.X * elapsedTime.Milliseconds;
            _player.velocity.Y += _player.acceleration.Y * elapsedTime.Milliseconds;

            _player.position.X += _player.velocity.X * elapsedTime.Milliseconds;
            _player.PushActorOutOfWalls(screen);
            _player.position.Y += _player.velocity.Y * elapsedTime.Milliseconds;
            _player.PushActorOutOfCeilings(screen);

            if (_player.velocity.Y > Actor.TerminalVelocityY)
                _player.velocity.Y = Actor.TerminalVelocityY;

            CheckTransitions(screen);

            return false;
        }

        protected void CheckTransitions(Screen screen)
        {
            var currentlyInTiles = new Tile[2]
            {
                screen.GetTileAt(_player.CenterX, _player.CenterY),
                screen.GetTileAt(_player.CenterX, _player.Bottom)
            };
            foreach (var currentlyInTile in currentlyInTiles)
            {
                if (currentlyInTile != null)
                {
                    Transistion? trans = screen.GetTransistionIfExists(currentlyInTile);

                    if (trans.HasValue)
                    {
                        // MegavaniaX.logger.DebugFormat("Transistion found while on tile ({0}, {1})", currentlyInTile.X, currentlyInTile.Y);
                        _player.DispatchEvent(new ScreenTransistionEvent(MegavaniaEventType.ScreenTransistionStart, trans.Value.Side, _player.position.X, _player.position.Y, null));
                        break;
                    }
                }
            }
        }

        protected bool HandleClimbEvent(ClimbEvent cevt)
        {
            // determine the lock-in position
            var rightTile = cevt.Screen.GetTileAt(_player.Right, _player.CenterY);
            if (rightTile.IsLadder)
            {
                NextState = new PlayerStateClimb(_player, cevt.Screen.GetTileCoords(rightTile).X, _player.position.Y);
                return true;
            }
            var leftTile = cevt.Screen.GetTileAt(_player.Left, _player.CenterY);
            if (leftTile.IsLadder)
            {
                NextState = new PlayerStateClimb(_player, cevt.Screen.GetTileCoords(leftTile).X, _player.position.Y);
                return true;
            }

            return false;
        }

        public abstract bool CanHandleEvent(MegavaniaEventType evtType);
        public abstract bool HandleEvent(IMegavaniaEvent evt);
        public abstract void OnExit();
        public virtual void StopShootingAnimation() { }


        #region Movement helper methods (common between a few states)

        protected void LeftPressed()
        {
            _player.IsFacingLeft = true;
            _player.velocity.X = -_player.defaultSpeedX;
        }

        protected void LeftReleased()
        {
            _player.velocity.X = 0;
        }

        protected void RightPressed()
        {
            _player.IsFacingLeft = false;
            _player.velocity.X = _player.defaultSpeedX;
        }

        protected void RightReleased()
        {
            _player.velocity.X = 0.0f;
        }

        #endregion


    }
}
